﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace KxParser.Parser.LR0
{
    class DFA
    {
        private List<State> _dfaStates = new List<State>();

        public DFA()
        {
            foreach (var productionItem in Global.ProductionItems)
            {
                if(productionItem.Name.Equals(Global.Starter))
                {
                    var lastValue = productionItem.Values.LastOrDefault();
                    if (!lastValue.Equals(Global.Ender))
                        productionItem.Values.Add(Global.Ender);

                    // Create initial state
                    var state = new State(_dfaStates.Count + 1);

                    var allItems = state.GetItemsPlusDependants(productionItem);

                    state.AddProductionItems(allItems);

                    _dfaStates.Add(state);

                    break; // Take first START
                }
            }

            CreateAllStates(_dfaStates[0]);

            PrintAll();
        }

        private void PrintAll()
        {
            foreach (var state in _dfaStates)
            {
                Console.WriteLine(state);
            }
        }

        //void CreateAllStates()
        //{
        //    if(_dfaStates.Count!=1) return; // In this phase we should have only initial state

        //    var statesQueue = new Queue<State>();
        //    statesQueue.Enqueue(_dfaStates[0]);

        //    while (statesQueue.Count > 0)
        //    {
        //        var currentState = statesQueue.Dequeue();

        //        var nextPaths = currentState.GetNextPaths();

        //        foreach (var nextPath in nextPaths)
        //        {
        //            State newState = null;
        //            foreach (var productionItem in currentState.GetProductionItems())
        //            {
        //                if(productionItem.CanConsume(nextPath))
        //                {
        //                    var newPid = productionItem.Consume(nextPath);

        //                    // Check all productionItems in all states
        //                    foreach (var theState in _dfaStates)
        //                    {
        //                        foreach (var theProductionitem in theState.GetProductionItems())
        //                        {
        //                            if(theProductionitem.Equals(newPid))
        //                            {
        //                                productionItem.NextState = theState;
        //                                productionItem.OverItem = nextPath;
        //                                break;
        //                            }
        //                        }
        //                        if(productionItem.NextState!=null)
        //                            break;
        //                    }

        //                    if (productionItem.NextState==null)
        //                    {
        //                        if (newState == null)
        //                        {
        //                            newState = new State(_dfaStates.Count + 1);
        //                            _dfaStates.Add(newState);
        //                        }

        //                        newState.AddProductionItem(newPid);
                                

        //                        statesQueue.Enqueue(newState);

        //                        productionItem.NextState = newState;
        //                        productionItem.OverItem = nextPath;
        //                    }
        //                }
        //            }
        //        }
        //    }
        //}

        void CreateAllStates(State state)
        {
            var pidsList = new List<ProductionItem>();


            if (state==null) return; // In this phase we should have only initial state

            var statesQueue = new Queue<State>();
            statesQueue.Enqueue(state);

            // Create new state for each possible transition
            while (statesQueue.Count > 0)
            {
                var currentState = statesQueue.Dequeue();

                var nextPaths = currentState.GetNextPaths();

                foreach (var nextPath in nextPaths)
                {
                    //State newState = null;

                    pidsList.Clear();

                    var selectedProductionItems = (from productionItem in currentState.GetProductionItems()
                                                   where productionItem.CanConsume(nextPath)
                                                   select productionItem);

                    //foreach (var productionItem in currentState.GetProductionItems())
                    //{
                    //    if (productionItem.CanConsume(nextPath))
                    //    {
                    //        pidsList.Add(productionItem.Consume(nextPath));
                    //    }
                    //} ==> LINQ

                    pidsList.AddRange(from productionItem in currentState.GetProductionItems()
                                      where productionItem.CanConsume(nextPath)
                                      select productionItem.Consume(nextPath));

                    bool compatibleStateFound = false;
                    foreach (var dfaState in _dfaStates)
                    {
                        var allItems = dfaState.GetItemsPlusDependants(pidsList.ToArray());
                        if (dfaState.CompatibleStateExists(allItems))
                        {
                            foreach (var productionItem in selectedProductionItems)
                            {
                                productionItem.NextState = dfaState;
                                productionItem.OverItem = nextPath;
                            }

                            compatibleStateFound = true;
                            break;
                        }
                            
                    }
                    

                    if(!compatibleStateFound)
                    {
                        var ns = new State(_dfaStates.Count + 1);

                        var allItems = ns.GetItemsPlusDependants(pidsList.ToArray());
                        ns.AddProductionItems(allItems);

                        foreach (var item in selectedProductionItems)
                        {
                            item.NextState = ns;
                            item.OverItem = nextPath;
                        }

                        _dfaStates.Add(ns);
                        statesQueue.Enqueue(ns);
                    }

                            // Check all productionItems in all states
                            //foreach (var theState in _dfaStates)
                            //{
                            //    foreach (var theProductionitem in theState.GetProductionItems())
                            //    {
                            //        if (theProductionitem.Equals(newPid))
                            //        {
                            //            productionItem.NextState = theState;
                            //            productionItem.OverItem = nextPath;
                            //            break;
                            //        }
                            //    }
                            //    if (productionItem.NextState != null)
                            //        break;
                            //})

                            //if (productionItem.NextState == null)
                            //{
                            //    if (newState == null)
                            //    {
                            //        newState = new State(_dfaStates.Count + 1); --
                            //        _dfaStates.Add(newState);
                            //    }

                            //    newState.AddProductionItem(newPid);--


                            //    statesQueue.Enqueue(newState);

                            //    productionItem.NextState = newState;--
                            //    productionItem.OverItem = nextPath;--
                            //}
                        
                    
                }
            }
        }

        public State[] GetStates()
        {
            return _dfaStates.ToArray();
        }
    }
}
